package com.liuyi.tools.io.poi;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.PageUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.liuyi.tools.WebUtil;
import com.liuyi.tools.io.ZipUtils;
import com.liuyi.tools.web.unusual.BizException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Supplier;

/**
 * <p>
 * excle导出类
 * <p>
 *
 * @ClassName ExcleTool
 * @Aulthor Mr.fmy
 * @Version 1.0
 * @Date 2019/8/27 17:32
 **/
@Slf4j
public class POIExportTool {

    /**
     * 拆分行数/如果 `大于` 该行数则拆分成多个excle
     * 最后以压缩文件返回、如果 `小于等于` 该行数、则
     * 返回 单个excle返回
     */
    private static final int limitation = 30000;

    /**
     * 自动截取拆分的数据
     */
    public static void exportLimitation(HttpServletResponse response, POIExport poiExport) {
        //1、如果导出的主要数据为空或长度为0 则直接进行http导出
        if (poiExport == null || poiExport.getData() == null || poiExport.getData().size() == 0) {
            throw new BizException("无数据");
        }
        //2、准备划算导出
        int dataSize = poiExport.getData().size();
        List<List<String>> data = poiExport.getData();
        String title = poiExport.getTitle();
        if (dataSize > limitation) {
            //按limitation拆分
            int num = dataSize % limitation == 0 ? dataSize / limitation : dataSize / limitation + 1;//得到拆分的数量
            List<File> files = new ArrayList<>(num);
            //循环数量
            try {
                for (int i = 0; i < num; i++) {
                    POIExport export = new POIExport();
                    BeanUtils.copyProperties(poiExport, export);
                    int start = limitation * i;
                    int end = 0;
                    if (i == num - 1) {
                        end = dataSize;
                    } else {
                        end = limitation * (i + 1);
                    }
                    export.setData(data.subList(start, end));
                    files.add(exportTemp(export, title + MessageFormat.format("第{0}~{1}行数据", start + 1, end)));
                }
                //压缩文件
                File zipFile = File.createTempFile(title, ".zip");
                ZipUtils.zipFiles(files.toArray(new File[num]), zipFile);
                //删除文件 -->如果过卡可以把这句搞成异步
                files.forEach(File::delete);
                //下载文件
                WebUtil.down(zipFile, title + ".zip", response);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                throw new BizException("导出失败");
            }

        } else {
            exportHttp(response, poiExport, poiExport.getTitle());
        }
    }

    public static <T> List<T> batches(Supplier<List<T>> supplier) {
        Page<T> pageHelper = PageHelper.startPage(1, 1000);
        List<T> batches = supplier.get();
        if (pageHelper.getTotal() > 1000) {//大于1000条、分批查询
            Integer page = PageUtil.totalPage((int) pageHelper.getTotal(), 1000);
            for (Integer i = 2; i <= page; i++) {
                PageHelper.startPage(i, 1000, false);
                batches.addAll(supplier.get());
            }
        }
        return batches;
    }

    public static <T> POIExport generateNumber(String title, List<String> cloumns, List<Map<String, Object>> list,
                                               List<String> keys) {
        return generateNumber(1, 1, title, cloumns, list, (item, itemList) -> {
            keys.forEach(key -> {
                Object value = item.get(key);
                itemList.add(ObjectUtil.isNotEmpty(value) ? value.toString() : "");
            });
        });
    }

    public static <T> POIExport generateNumber(String title, List<String> cloumns, List<T> list, BiConsumer<T,
            List<String>> biConsumer) {
        return generateNumber(1, 1, title, cloumns, list, biConsumer);
    }

    /**
     * 快速生成 {@link POIExport} 方法
     * <p>
     * 带序号的
     * </p>
     *
     * @param startNum   序号开始位
     * @param numSection 序号自增量
     * @param list       数据集合
     * @param biConsumer 自定义处理方法
     * @param <T>
     */
    public static <T> POIExport generateNumber(Integer startNum, Integer numSection, String title, List<String>
            cloumns, List<T> list, BiConsumer<T, List<String>> biConsumer) {
        Assert.notNull(startNum, "startNum is not null");
        Assert.notNull(numSection, "numSection is not null");
        List<List<String>> datas = new ArrayList<>();
        Integer num = startNum;
        for (T item : list) {
            List<String> dataItem = new ArrayList<>();
            dataItem.add(num.toString());//编号
            biConsumer.accept(item, dataItem);
            datas.add(dataItem);
            num += numSection;
        }
        return new POIExport(title, cloumns, datas);
    }

    /**
     * 快速生成 {@link POIExport} 方法
     *
     * @param list       数据集合
     * @param biConsumer 自定义处理方法
     * @param <T>
     */
    public static <T> POIExport generate(String title, List<String> cloumns, List<T> list, BiConsumer<T,
            List<String>> biConsumer) {
        List<List<String>> datas = new ArrayList<>();
        list.forEach(item -> {
            List<String> dataItem = new ArrayList<>();
            biConsumer.accept(item, dataItem);
            datas.add(dataItem);
        });
        return new POIExport(title, cloumns, datas);
    }

    public static POIExport generateKeyCameCase(String title, List<String> cloumns, List<Map<String, Object>> list,
                                                List<String> keys) {
        List<String> keyCamelCase = new ArrayList<>(keys.size());
        keys.forEach(key -> keyCamelCase.add(StrUtil.toCamelCase(key)));
        return generate(title, cloumns, list, keyCamelCase);
    }

    public static POIExport generate(String title, List<String> cloumns, List<Map<String, Object>> list, List<String>
            keys) {
        List<List<String>> datas = new ArrayList<>();
        list.forEach(item -> {
            List<String> dataItem = new ArrayList<>();
            keys.forEach(key -> {
                Object value = item.get(key);
                if (ObjectUtil.isNotEmpty(value)) {
                    dataItem.add(value.toString());
                } else {
                    dataItem.add("");
                }
            });
            datas.add(dataItem);
        });
        return new POIExport(title, cloumns, datas);
    }

    /**
     * excle生成方法
     */
    private static ExcelWriter exportOrdinary(POIExport poiExport) {
        ExcelWriter writer = ExcelUtil.getBigWriter();
        writer.merge(poiExport.getCloumnNum() - 1, poiExport.getTitle());//合并单元格输出标题
        writer.writeRow(poiExport.getCloumns());//写入标题
        writer.write(poiExport.getData());//写入数据
        return writer;
    }

    /**
     * http导出工具
     */
    private static void exportHttp(HttpServletResponse response, POIExport poiExport, String name) {
        //导出
        try {
            export(response, exportOrdinary(poiExport), name);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new BizException("导出失败");
        }
    }

    /**
     * 临时文件导出
     */
    private static File exportTemp(POIExport poiExport, String name) {
        //导出
        try {
            return export(exportOrdinary(poiExport), name);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new BizException("导出失败");
        }
    }

    /**
     * http输出流方式
     *
     * @param response
     * @throws IOException
     */
    private static void export(HttpServletResponse response, ExcelWriter ew, String title) throws IOException {
        OutputStream output = null;
        try {
            output = response.getOutputStream();
            response.reset();
            response.setHeader("Content-disposition", "attachment; filename=" + URLEncoder.encode(title + ".xls",
                    "UTF-8"));
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Allow-Method", "GET");
            response.setHeader("Access-Control-Allow-Method", "*");
            //response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            response.setContentType("application/msexcel");
            ew.flush(output);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new BizException("导出失败");
        } finally {
            if (ew != null) {
                ew.close();
            }
            if (output != null) {
                output.close();
            }
        }
    }

    /**
     * 临时文件方式
     *
     * @param title
     * @throws IOException
     */
    private static File export(ExcelWriter ew, String title) throws IOException {
        File file = File.createTempFile(title, ".xls");
        OutputStream output = null;
        file.deleteOnExit();//jvm退出时删除
        try {
            output = new FileOutputStream(file);
            ew.flush(output);
            output.flush();
            return file;
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new BizException("导出失败");
        } finally {
            if (ew != null) {
                ew.close();
            }
            if (output != null) {
                output.close();
            }
        }
    }


    public static void export(HttpServletResponse response, List<String> columnList, List<Map<String,Object>> dataList, String fileName,
                              List<String> keys) {
        POIExportTool.exportLimitation(response, POIExportTool.generate(fileName, columnList, dataList, keys));
    }


}
